package com.alks.common.service.serviceImpl;

import com.alibaba.fastjson2.JSON;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.entity.data.enums.CompanyEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.alks.entity.data.enums.RedisFormatEnum.REDIS_TIME_OUT;


@Slf4j
@Service
public final class RedisServiceImpl implements RedisService {
    /**
     * 过期时长 设置为两个小时
     */
    private final Long DURATION = 60 * 60 * 12 * 1000L;

    @Resource
    private RedisTemplate redisTemplate;



    private ValueOperations<String, String> valueOperations;

    @PostConstruct
    public void init() {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        redisTemplate.setHashValueSerializer(redisSerializer);
        valueOperations = redisTemplate.opsForValue();
    }

    @Override
    public void set(String key, String value) {
        valueOperations.set(key, value, DURATION, TimeUnit.MILLISECONDS);
//        log.info("key={}, value is: {} into redis cache", key, value);
    }

    @Override
    public void set(String key, String value, long expireTime) {
        valueOperations.set(key, value, expireTime, TimeUnit.SECONDS);
    }

    @Override
    public String get(String key) {
        String redisValue = valueOperations.get(key);
        return redisValue;
    }

    @Override
    public boolean delete(String key) {
        boolean result = redisTemplate.delete(key);
        log.debug("delete from redis, key is: {}", key);
        return result;
    }

    @Override
    public Long getExpireTime(String key) {
        return valueOperations.getOperations().getExpire(key);
    }

    @Override
    public Set scan(String key) {
        return redisTemplate.keys(key);
    }

    @Override
    public RedisConnectionFactory getConnectionFactory() {
        return redisTemplate.getConnectionFactory();
    }

    /*设置bitMap*/
    @Override
    public void setBit(String key, long offset, boolean value) {
        ValueOperations<String, Boolean> ops = redisTemplate.opsForValue();
        ops.setBit(key, offset, value);

    }

    /*取出bitMap*/
    @Override
    public boolean getBit(String key, long offset) {
        ValueOperations<String, Boolean> ops = redisTemplate.opsForValue();
        return Boolean.TRUE.equals(ops.getBit(key, offset));
    }

    /*通过bitPos找到BitMap的第一个0*/
    @Override
    public long findZero(String key) {
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        try {
            return connection.bitPos(key.getBytes(), false);
        } finally {
            connection.close();
        }
    }


    @Override
    public void setOutTime(String key, long timeOut, TimeUnit timeUnit) {
        if (timeOut == -1) {
            redisTemplate.persist(key);
        } else {
            redisTemplate.expire(key, timeOut, timeUnit);
        }
    }

    @Override
    public void setOutTime(String key, long timeOut) {
        if (timeOut == -1) {
            redisTemplate.persist(key);
        } else {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }

    @Override
    public void setOutTime(String key) {
        redisTemplate.expire(key,REDIS_TIME_OUT.getValue(),TimeUnit.HOURS);
    }

    @Override
    public String slipNoCreat(String companyId,String k, int length) {
        companyId = companyId ==null?"":companyId;
        String key = companyId + k + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMM"));
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%0"+length+"d", ato.getAndIncrement());
        this.setOutTime(key);
        return key+value;
    }

    @Override
    public String slipNoCreat(String k, int length) {
        return slipNoCreat(UserIdThread.get().getCompanyId(),k,length);
    }

    @Override
    public String barcodeCreat(String k, int length) {
        String key = k + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%0"+length+"d", ato.incrementAndGet());
        this.setOutTime(key);
        return key+value;
    }

    @Override
    public String barcodeCreatTwo(String k, int length) {
        String key = k + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%0"+length+"d", ato.incrementAndGet());
        this.setOutTime(key);
        return key+value;
    }

    @Override
    public String barcodeCreatAdd(String k, int length) {
        RedisAtomicInteger ato = new RedisAtomicInteger(k, this.getConnectionFactory());
        String value = String.format("%0"+length+"d",ato.incrementAndGet());
        return k+value;
    }


    @Override
    public String slipNoCreat(String k) {
        String companyId = UserIdThread.get().getCompanyId();
        String key = CompanyEnum.updateId(companyId) + k + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMM"));
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%05d", ato.getAndIncrement());
        this.setOutTime(key);
        return key+value;
    }

    /**
     * 更新订单序列号，避免Redis重启导致的重复问题
     * @param orderKey 订单key前缀
     * @param currentOrderNumber 当前订单号
     * @param length 序列号长度
     * @return 新的订单号
     */
    @Override
    public String updateOrderNumber(String orderKey, String currentOrderNumber, int length) {
        // 从当前订单号中提取序列号部分
        String sequencePart = extractSequenceFromOrderNumber(currentOrderNumber, length);

        // 构造Redis key
        String companyId = UserIdThread.get().getCompanyId();
        String key = CompanyEnum.updateId(companyId) + orderKey + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMM"));

        // 初始化RedisAtomicInteger
        RedisAtomicInteger atomicInteger = new RedisAtomicInteger(key, this.getConnectionFactory());

        // 如果当前序列号大于Redis中的值，则更新Redis中的值
        int currentSequence = Integer.parseInt(sequencePart);
        int redisSequence = atomicInteger.get();

        if (currentSequence >= redisSequence) {
            atomicInteger.set(currentSequence + 1);
        }

        // 生成新的序列号
        String newValue = String.format("%0" + length + "d", atomicInteger.getAndIncrement());
        this.setOutTime(key);

        // 构造新的订单号
        String newOrderNumber = key + newValue;
        return newOrderNumber;
    }

    /**
     * 从订单号中提取序列号部分
     * @param orderNumber 完整订单号
     * @param length 序列号长度
     * @return 序列号部分
     */
    private String extractSequenceFromOrderNumber(String orderNumber, int length) {
        if (orderNumber == null || orderNumber.length() < length) {
            return "0";
        }
        // 提取最后length位作为序列号
        return orderNumber.substring(orderNumber.length() - length);
    }

    @Override
    public String slipNoCreatByTime(String k) {
        // 1.获取公司ID
        String companyId = UserIdThread.get().getCompanyId();

        // 2.构造Redis计数器的key
        String key = CompanyEnum.updateId(companyId) + k + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMM"));

        //3.构建值得前缀：公司ID+key+时间戳
        // 使用精确到秒的时间戳（格式：yyyyMMddHHmmss）
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"));
        //构建值的前缀：
        String prefix = CompanyEnum.updateId(companyId) + k + timestamp;

        // 创建RedisAtomicInteger实例并获取下一个自增数字
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%03d", ato.getAndIncrement()); // 自增数字，宽度为3位

        this.setOutTime(key);
        return prefix + value;
    }

    @Override
    public String slipNoGenerate(String k, int length,int type) {
        String key = generateKey(k, type);
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String value = String.format("%0"+length+"d", ato.getAndIncrement());
        // 获取当前时间
        LocalDateTime currentTime = LocalDateTime.now();
        // 获取本月最后一天的日期
        LocalDateTime lastDayOfMonth = currentTime.with(TemporalAdjusters.lastDayOfMonth());
        // 计算当前时间距离本月结束的剩余时间
        LocalDateTime endOfDay = lastDayOfMonth.with(LocalTime.MAX);
        long remainingMinutes = ChronoUnit.MINUTES.between(currentTime, endOfDay);
        this.setOutTime(key,remainingMinutes,TimeUnit.MINUTES);
        return key+value;
    }

    /**
     * 获得时间戳+自增数据
     * @param key      根据key键，获得自增数据
     * @param length   自增长度,如：填7，就是7位数字自增
     * @param timezone 想要获得的时间戳保留字段 : yyyyMMddHHmmss (yyyy-MM-dd HH:mm:ss)
     */
    @Override
    public String numberGenerate(String key, String length, String timezone) {
        //1.根据key获得自增数据
        RedisAtomicInteger ato = new RedisAtomicInteger(key, this.getConnectionFactory());
        String l = "%0" + length + "d"; //%07d
        String value = String.format(l, ato.getAndIncrement());
        this.setOutTime(key);

        //2.获得自定义时间戳
        LocalDateTime now = LocalDateTime.now();
        String time = now.format(DateTimeFormatter.ofPattern(timezone));

        //3.拼接
        String result = time + value;
        return result;
    }

    @Override
    public void rmOutTime(String key) {
        redisTemplate.expire(key, 0L, TimeUnit.SECONDS);
    }

    @Override
    public void setHash(String key, String hashKey, String value) {
        HashOperations<String,String, String> valueOps = redisTemplate.opsForHash();
        valueOps.put(key, hashKey, value);
    }

    @Override
    public void setHash(String key, String hashKey, List<String> value) {
        String jsonString = JSON.toJSONString(value);
        HashOperations<String,String, String> valueOps = redisTemplate.opsForHash();
        valueOps.put(key, hashKey, jsonString);
    }

    @Override
    public void addHashAsSet(String key, String hashKey, String value) {
        /*获取原先存在的数据 补充数据*/
        Set<String> hashAsList = getHashAsSet(key, hashKey);
        hashAsList.add(value);
        /*写入补充后的数据*/
        String jsonString = JSON.toJSONString(hashAsList);
        HashOperations<String,String, String> valueOps = redisTemplate.opsForHash();
        valueOps.put(key, hashKey, jsonString);
    }

    @Override
    public String getHash(String key, String hashKey) {
        HashOperations<String,String, String> valueOps = redisTemplate.opsForHash();
        return valueOps.get(key, hashKey);
    }

    @Override
    public void deleteHash(String key, String hashKey) {
        HashOperations<String, String, String> valueOps = redisTemplate.opsForHash();
        valueOps.delete(key, hashKey);
    }

    @Override
    public Set<String> getHashAsSet(String key, String hashKey) {
        HashOperations<String,String,String> valueOps = redisTemplate.opsForHash();
        List<String> list = JSON.parseArray(valueOps.get(key, hashKey), String.class);
        list=list==null?new ArrayList<>():list;
        return new HashSet<>(list);
    }

    /**
     * 生成key
     * @param k 内键
     * @param type 类型 1.yyyyMM类型 2.yymm类型
     * @return 生成的key
     */
    private String generateKey(String k, int type) {
        String pattern;
        switch (type) {
            case 1:
                pattern =  DateTimeFormatter.ofPattern("yyyyMM").format(LocalDate.now());
                break;
            default:
                pattern =  DateTimeFormatter.ofPattern("yyMM").format(LocalDate.now());
                break;
        }
        return k + pattern;
    }

    @Override
    public Boolean setIfAbsent(String key, String value, long expireTime, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
    }

    @Override
    public boolean setExpireTime(String key, long expireTime, TimeUnit timeUnit) {
        return redisTemplate.expire(key, expireTime, timeUnit);
    }

    @Override
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    @Override
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    @Override
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    @Override
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    @Override
    public boolean deleteCacheMapValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    @Override
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public long hashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    @Override
    public void deleteIfHashEmpty(String key) {
        if (hashSize(key) == 0) {
            redisTemplate.delete(key);
        }
    }

    @Override
    public HashOperations<String, String, String> opsForHash() {
        return redisTemplate.opsForHash();
    }

    @Override
    public long incr(String redisKey, int i) {
        return redisTemplate.opsForValue().increment(redisKey, i);
    }
}